#pragma once
/*
日志落地模块实现
    1. 抽象落地基类
    2. 派生落地子类（根据不同的方向进行派生）
    3. 使用工厂模式进行创建与表示的分离
sink:下沉/落地
*/
#include "util.hpp"
#include "message.hpp"
#include "formatter.hpp"
#include <memory>
#include <mutex>

namespace hsl_log
{
    // 日志落地基类，定义统一的日志输出接口  
    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;//智能指针的别名（==typedef...）
        LogSink() {}
        virtual ~LogSink() {}
        virtual void log(const char *data, size_t len) = 0;// 纯虚函数，定义日志输出接口
    };
    // 标准输出
    class StdoutSink : public LogSink
    {
    public:
        using ptr = std::shared_ptr<StdoutSink>;//
        StdoutSink() = default;// 默认构造函数
        void log(const char *data, size_t len) override
        {
            std::cout.write(data, len);
        }
    };
    // 指定文件
    class FileSink : public LogSink
    {
    public:
        using ptr = std::shared_ptr<FileSink>;

        FileSink(const std::string &filename) : _filename(filename)
        {
            // 1.创建日志所在的目录
            util::file::create_directory(util::file::path(filename));
            // 2.创建并打开日志文件（app追加形式）
            _ofs.open(_filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        const std::string &file() { return _filename; }

        void log(const char *data, size_t len)
        {
            _ofs.write((const char *)data, len);
            if (_ofs.good() == false)
            {
                std::cout << "日志输出文件失败！\n";
            }
        }

    private:
        std::string _filename;
        std::ofstream _ofs;
    };
    //滚动文件
    class RollSink : public LogSink
    {
    public:
        using ptr = std::shared_ptr<RollSink>;
        RollSink(const std::string &basename, size_t max_fsize) 
        : _basename(basename), _max_fsize(max_fsize), _cur_fsize(0),_name_count(0)
        {
            util::file::create_directory(util::file::path(basename));
        }
        void log(const char *data, size_t len)
        {
            initLogFile();
            _ofs.write(data, len);
            if (_ofs.good() == false)
            {
                std::cout << "日志输出文件失败！\n";
            }
            _cur_fsize += len;
        }

    private:
        // 初始化日志文件，检查是否需要创建新文件
        void initLogFile()
        {
            // 如果文件未打开或当前文件大小超过限制，则创建新文件
            if (_ofs.is_open() == false || _cur_fsize >= _max_fsize)
            {
                _ofs.close();
                std::string name = createFilename();
                _ofs.open(name, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_fsize = 0;
                return;
            }
            return;
        }
        //用时间戳当文件名，保证文件名的唯一性
        std::string createFilename()
        {
            time_t t = time(NULL);
            struct tm lt;
            localtime_r(&t, &lt);
            std::stringstream ss;
            ss << _basename;
            ss << lt.tm_year + 1900;
            ss << lt.tm_mon + 1;
            ss << lt.tm_mday;
            ss << lt.tm_hour;
            ss << lt.tm_min;
            ss << lt.tm_sec;
            ss << "-";
            ss << _name_count++;
            ss << ".log";
            return ss.str();
        }

    private:
        size_t _name_count;
        std::string _basename;
        std::ofstream _ofs;
        size_t _max_fsize;
        size_t _cur_fsize;
    };
    // 日志落地工厂类，用于创建各种类型的日志落地器
    class SinkFactory
    {
    public:
        // 工厂方法，创建指定类型的日志落地器
        // 模板参数:
        //   SinkType: 要创建的落地器类型
        //   Args: 构造参数类型
        // 参数:
        //   args: 传递给构造函数的参数
        // 返回值: 落地器的共享指针
        template <typename SinkType, typename... Args>
        static LogSink::ptr create(Args &&...args)
        {
            //使用完美转发创建指定类型的落地器
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };

}